﻿using System;
using System.Collections.Generic;
using System.Linq;
using DataModel.Models;
using DataModel.Managers.SubManage;

namespace DataModel.Managers
{
    public class JournalManager
    {
        private readonly UniversityPortalEntities _entityModel;

        /// <summary>
        /// Create new Entity in Constructor
        /// </summary>
        public JournalManager()
        {
            _entityModel = new UniversityPortalEntities();
        }

        /// <summary>
        /// Take new model from Outer
        /// </summary>
        /// <param name="entity"></param>
        public JournalManager(UniversityPortalEntities entity)
        {
            _entityModel = entity;
        }

        #region GetSomeInformationRegion

        /// <summary>
        /// Get group journal Id
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <returns></returns>
        public Journal GetGroupJournal(Int64 groupNumber)
        {
            Journal journal = null;
            Group group = _entityModel.Groups.FirstOrDefault(groupItem => groupItem.number == groupNumber);
            if (group != null)
            {
                journal = group.Journals.FirstOrDefault(journalItem => journalItem != null);
            }
            return journal;
        }

        /// <summary>
        /// Get All cells in the group journal
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByGroup(Int64 groupNumber)
         {
            Journal journal = GetGroupJournal(groupNumber);
           //Find All Lessons with Need Subject
           return from jcell in _entityModel.JournalCells
                           where (jcell.journalID == journal.ID)
                           select jcell;
         }

        /// <summary>
        /// Get cell for some Date
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByGroup(Int64 groupNumber, DateTime date)
         {
            Journal journal = GetGroupJournal(groupNumber);
           //Find All Lessons with Need Subject
           return from jcell in _entityModel.JournalCells
                           where (jcell.journalID == journal.ID && jcell.date == date)
                           select jcell;
         }

        /// <summary>
        /// Get Cells Before or After some Date
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="date"></param>
        /// <param name="beforeDate"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByGroup(Int64 groupNumber, DateTime date, bool beforeDate)
         {
            Journal journal = GetGroupJournal(groupNumber);
           //Find All Lessons with Need Subject
           return (beforeDate) ? from jcell in _entityModel.JournalCells where (jcell.journalID == journal.ID && jcell.date <= date ) select jcell
                                :from jcell in _entityModel.JournalCells where (jcell.journalID == journal.ID && jcell.date >= date) select jcell;
         }

        /// <summary>
        ///  return All cells in group journal on current subject
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="subject"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByGroup(Int64 groupNumber, Subject subject)
        {
            Journal journal = GetGroupJournal(groupNumber);
           //Find All Lessons with Need Subject
            var needLessons = from l in _entityModel.Lessons
                              where l.subjectID == subject.ID
                              select l;

           return from jcell in _entityModel.JournalCells
                           from nLess in needLessons
                           where (jcell.journalID == journal.ID && jcell.lessonID == nLess.ID)
                           select jcell;
        }

        /// <summary>
        /// return All cells in group journal on current subject
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="subject"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByGroup(Int64 groupNumber, Subject subject, DateTime date)
        {
            return from jcell in this.GetJournalCellsByGroup(groupNumber, subject)
                   where jcell.date == date
                   select jcell;
        }

        /// <summary>
        /// Get journalCells by group at begin to end date (order cells by date)
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="subject"></param>
        /// <param name="begin"></param>
        /// <param name="end"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByGroup(Int64 groupNumber, Subject subject, DateTime begin, DateTime end)
        {
            return from jcell in this.GetJournalCellsByGroup(groupNumber, subject)
                   where (begin >= jcell.date) && (jcell.date <= end)
                   orderby jcell.date
                   select jcell;
        }

        /// <summary>
        /// Get All Cell in group journal where date before or after some dateTime
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="subject"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByGroup(Int64 groupNumber, Subject subject, DateTime date, bool beforeDate)
        {
            return (beforeDate) ? from jcell in this.GetJournalCellsByGroup(groupNumber, subject) where jcell.date <= date select jcell
                                : from jcell in this.GetJournalCellsByGroup(groupNumber, subject) where jcell.date >= date select jcell;
        }

        /// <summary>
        /// Get All journal cells for current studetn in group journal
        /// </summary>
        /// <param name="studentID"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByStudent(Guid studentID)
        {
            return from jcells in _entityModel.JournalCells where jcells.studentID == studentID select jcells;
        }

        /// <summary>
        /// Get all journal cells for current student in group journal
        /// </summary>
        /// <param name="studentID"></param>
        /// <param name="subjectID"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByStudent(Guid studentID, Guid subjectID)
        {
            //Find All Lessons with Need Subject
            var needLessons = from l in _entityModel.Lessons where l.subjectID == subjectID select l;

            return from jcell in _entityModel.JournalCells from nLess in needLessons where jcell.lessonID == nLess.ID && jcell.studentID == studentID select jcell;

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="studentID"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByStudent(Guid studentID, DateTime date)
        {
            return from jcells in GetJournalCellsByStudent(studentID) where jcells.date == date select jcells;
        }

        /// <summary>
        /// Get all journal cells for current student date before or after some date
        /// </summary>
        /// <param name="studentID"></param>
        /// <param name="date"></param>
        /// <param name="beforeDate"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByStudent(Guid studentID, DateTime date, bool beforeDate)
        {
            return (beforeDate) ? from jcells in GetJournalCellsByStudent(studentID) where jcells.date <= date select jcells
                : from jcells in GetJournalCellsByStudent(studentID) where jcells.date >= date select jcells;
        }

        /// <summary>
        /// Get journal cells for current student and subject where date == some date
        /// </summary>
        /// <param name="studentID"></param>
        /// <param name="subject"></param>
        /// <param name="date"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByStudent(Guid studentID, Subject subject, DateTime date)
        {
            return from jcell in GetJournalCellsByStudent(studentID, subject.ID) where jcell.date == date select jcell;
        }

        /// <summary>
        /// Get journal cells for current student and subject where date after or before some date
        /// </summary>
        /// <param name="studentID"></param>
        /// <param name="subject"></param>
        /// <param name="date"></param>
        /// <param name="beforeDate"></param>
        /// <returns></returns>
        private IQueryable<JournalCell> GetJournalCellsByStudent(Guid studentID, Subject subject, DateTime date, bool beforeDate)
        {
            return (beforeDate) ? from jcell in GetJournalCellsByStudent(studentID, subject.ID) where jcell.date <= date select jcell
                : from jcell in GetJournalCellsByStudent(studentID, subject.ID) where jcell.date >= date select jcell;
        }
        
        /// <summary>
        /// Get group journal by semester
        /// </summary>
        /// <param name="semester"></param>
        /// <param name="journalCells"></param>
        /// <returns>Journam Model => include All journal cells</returns>
        private JournalModel GetJournalByGroup(IQueryable<JournalCell> journalCells,int semester)
        {
            //We didn't find needed cells
            if (journalCells == null || journalCells.Count() == 0) return null;

            JournalModel journalModel = new JournalModel() { JournalRows = new List<JournalRowModel>() };
            journalModel.Group = journalCells.First(jc => jc != null).Journal.Group.number;

            //Action default give journal by Current semester
            semester = (semester == 0) ? journalCells.First(jc => jc != null).Journal.Group.GetCurrentSemester() : semester;

            foreach (var jCell in journalCells)
            {
                
                if (jCell.Lesson.semester == semester)
                {
                    JournalCellModel jCellModel = new JournalCellModel()
                    {
                        Date = jCell.date,
                        Mark = jCell.mark,
                        Presence = jCell.presence,
                        Lesson = new LessonModel()
                        {
                            Subject = jCell.Lesson.Subject,
                            Professor = jCell.Lesson.Professor,
                            LessonType = jCell.Lesson.LessonType,
                            LessonID = jCell.lessonID,
                            EndTime = new DateTime(jCell.Lesson.endTime.Ticks),
                            BeginTime = new DateTime(jCell.Lesson.beginTime.Ticks),
                            Auditory = jCell.Lesson.Auditory
                        }
                    };
                }
            }

            return journalModel;
        }
        /*
        /// <summary>
        ///  Get group journalModel by semester
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="semester"></param>
        /// <returns></returns>
        public JournalModel GetJournalByGroup(Int64 groupNumber, int semester = 0)
        {
            return GetJournalByGroup(GetJournalCellsByGroup(groupNumber), semester);
        }

        /// <summary>
        ///  Get group journalModel by subject and semester
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="subject"></param>
        /// <param name="semester"></param>
        /// <returns></returns>
        public JournalModel GetJournalByGroup(Int64 groupNumber, Subject subject, int semester = 0)
        {
            return GetJournalByGroup(GetJournalCellsByGroup(groupNumber,subject), semester);
        }

        /// <summary>
        ///  Get group journalModel by date and semester
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="date"></param>
        /// <param name="semester"></param>
        /// <returns></returns>
        public JournalModel GetJournalByGroup(Int64 groupNumber, DateTime date,int semester = 0)
        {
            return GetJournalByGroup(GetJournalCellsByGroup(groupNumber,date), semester);
        }

        /// <summary>
        /// Get group journalModel by subject ,date and semester
        /// </summary>
        /// <param name="groupNumber"></param>
        /// <param name="subject"></param>
        /// <param name="date"></param>
        /// <param name="semester"></param>
        /// <returns></returns>
        public JournalModel GetJournalByGroup(Int64 groupNumber,Subject subject, DateTime date,int semester = 0)
        {
            return GetJournalByGroup(GetJournalCellsByGroup(groupNumber,subject,date), semester);
        }
        */

        
        #endregion

        public JournalModel GetJournalByGroup(Int64 groupNumber, Subject subject, DateTime beginDate, DateTime endDate)
        {
            if (beginDate > endDate) return null;

            Group currentGroup = _entityModel.Groups.First(g => g.number == groupNumber);
            IEnumerable<Student> students = currentGroup.Students.OrderBy(s => s.User.lastName).Select(s => s);
            IEnumerable<Lesson> lessonOnSubject = currentGroup.Lessons.Where(l => l.subjectID == subject.ID);
            var journalModel = new JournalModel()
            {
                JournalRows = new List<JournalRowModel>(),
                Group = groupNumber,
                DateList = new List<DateTime>(),
                Subject = new SubjectModel()
                {
                    SubjectID = subject.ID,
                    ShortName = subject.shortName,
                    FullName = subject.fullName,
                    Desription = subject.desription
                }
            };
            
            //Create list of date by lessons with current Subject
            do
            {
                foreach(var lesson in lessonOnSubject)
                {
                    if(ScheduleManager.LessonInDate(beginDate,lesson))
                    {
                        journalModel.DateList.Add(beginDate);
                    }
                }

                beginDate = beginDate.AddDays(1);

            } while (beginDate <= endDate);
            

            foreach (var student in students)
            {
                var sortCells = student.JournalCells.OrderBy(jc => jc.date).Select(c => c);
                JournalRowModel jRModel = new JournalRowModel() { JournalCells = new List<JournalCellModel>(), Student = student };

                foreach(var cell in sortCells)
                {
                    jRModel.JournalCells.Add(new JournalCellModel()
                    {
                        Date = cell.date,
                        Mark = cell.mark,
                        Presence = cell.presence,
                        Lesson = new LessonModel()
                        {
                            Subject = cell.Lesson.Subject,
                            Professor = cell.Lesson.Professor,
                            LessonType = cell.Lesson.LessonType,
                            LessonID = cell.lessonID,
                            EndTime = new DateTime(cell.Lesson.endTime.Ticks),
                            BeginTime = new DateTime(cell.Lesson.beginTime.Ticks),
                            Auditory = cell.Lesson.Auditory
                        }
                    });

                }
                journalModel.JournalRows.Add(jRModel);
            }

            return journalModel;
        }

        public JournalModel GetJournalByGroup(Int64 groupNumber, SubjectModel subject, DateTime beginDate, DateTime endDate)
        {
            if (beginDate > endDate) return null;

            Group currentGroup = _entityModel.Groups.First(g => g.number == groupNumber);
            IEnumerable<Student> students = currentGroup.Students.OrderBy(s => s.User.lastName).Select(s => s);
            IEnumerable<Lesson> lessonOnSubject = currentGroup.Lessons.Where(l => l.subjectID == subject.SubjectID);
            var journalModel = new JournalModel()
            {
                JournalRows = new List<JournalRowModel>(),
                Group = groupNumber,
                DateList = new List<DateTime>(),
                Subject = subject
            };

            //Create list of date by lessons with current Subject
            do
            {
                foreach (var lesson in lessonOnSubject)
                {
                    if (ScheduleManager.LessonInDate(beginDate, lesson))
                    {
                        journalModel.DateList.Add(beginDate);
                    }
                }

                beginDate = beginDate.AddDays(1);

            } while (beginDate <= endDate);


            foreach (var student in students)
            {
                var sortCells = student.JournalCells.OrderBy(jc => jc.date).Select(c => c);
                JournalRowModel jRModel = new JournalRowModel() { JournalCells = new List<JournalCellModel>(), Student = student };

                foreach (var cell in sortCells)
                {
                    jRModel.JournalCells.Add(new JournalCellModel()
                    {
                        Date = cell.date,
                        Mark = cell.mark,
                        Presence = cell.presence,
                        Lesson = new LessonModel()
                        {
                            Subject = cell.Lesson.Subject,
                            Professor = cell.Lesson.Professor,
                            LessonType = cell.Lesson.LessonType,
                            LessonID = cell.lessonID,
                            EndTime = new DateTime(cell.Lesson.endTime.Ticks),
                            BeginTime = new DateTime(cell.Lesson.beginTime.Ticks),
                            Auditory = cell.Lesson.Auditory
                        }
                    });

                }
                journalModel.JournalRows.Add(jRModel);
            }

            return journalModel;
        }

        #region AddCellRegion

        /// <summary>
        /// Create new cell in group journal
        /// </summary>
        /// <param name="studID"></param>
        /// <param name="lessonID"></param>
        /// <param name="date"></param>
        /// <param name="presence"></param>
        public bool SetPresenceOrMark(Guid studID, Guid subjectID,DateTime date, int? presence,int? mark = null)
        {
            try
            {
                Student student = _entityModel.Students.FirstOrDefault(s => s.ID == studID);
                Lesson lesson = student.Group.Lessons.FirstOrDefault(l => l.subjectID == subjectID && ScheduleManager.LessonInDate(date, l));
                JournalCell jCell = student.JournalCells.FirstOrDefault(jc => jc.lessonID == lesson.ID && jc.date.CompareOnlyDate(date));

                if (jCell == null)
                {
                    Journal journal = student.Group.Journals.FirstOrDefault(j => j != null);
                    JournalCell journalCell = JournalCell.CreateJournalCell(Guid.NewGuid(), studID, date, lesson.ID, journal.ID);
                    journalCell.presence = presence;
                    journalCell.mark = mark;
                    _entityModel.AddToJournalCells(journalCell);
                }
                else if (mark == null)
                {
                    jCell.presence = presence;
                }
                else
                {
                    jCell.mark = mark;
                    jCell.presence = presence;
                }

                _entityModel.SaveChanges();
            }
            catch
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Set only presence
        /// </summary>
        /// <param name="studID"></param>
        /// <param name="subjectID"></param>
        /// <param name="date"></param>
        /// <param name="presence"></param>
        /// <returns></returns>
        public bool SetPresence(Guid studID, Guid subjectID, DateTime date, int? presence)
        {
            try
            {
                Student student = _entityModel.Students.FirstOrDefault(s => s.ID == studID);
                Lesson lesson = student.Group.Lessons.FirstOrDefault(l => l.subjectID == subjectID && ScheduleManager.LessonInDate(date, l));
                JournalCell jCell = student.JournalCells.FirstOrDefault(jc => jc.lessonID == lesson.ID && jc.date.CompareOnlyDate(date));

                if (jCell == null)
                {
                    Journal journal = student.Group.Journals.FirstOrDefault(j => j != null);
                    JournalCell journalCell = JournalCell.CreateJournalCell(Guid.NewGuid(), studID, date, lesson.ID, journal.ID);
                    journalCell.presence = presence;
                    _entityModel.AddToJournalCells(journalCell);
                }
                else
                {
                    jCell.presence = presence;
                }

                _entityModel.SaveChanges();
            }
            catch
            {
                return false;
            }
            return true;
        }

        /// <summary>
        /// Set only mark on Subject 
        /// </summary>
        /// <param name="studID"></param>
        /// <param name="subjectID"></param>
        /// <param name="date"></param>
        /// <param name="mark"></param>
        /// <returns></returns>
        public bool SetMark(Guid studID, Guid subjectID, DateTime date, int? mark)
        {
            try
            {
                Student student = _entityModel.Students.FirstOrDefault(s => s.ID == studID);
                Lesson lesson = student.Group.Lessons.FirstOrDefault(l => l.subjectID == subjectID && ScheduleManager.LessonInDate(date, l));
                JournalCell jCell = student.JournalCells.FirstOrDefault(jc => jc.lessonID == lesson.ID && jc.date.CompareOnlyDate(date));

                if (jCell == null)
                {
                    Journal journal = student.Group.Journals.FirstOrDefault(j => j != null);
                    JournalCell journalCell = JournalCell.CreateJournalCell(Guid.NewGuid(), studID, date, lesson.ID, journal.ID);
                    journalCell.mark = mark;
                    _entityModel.AddToJournalCells(journalCell);
                }
                else
                {
                    jCell.mark = mark;
                }

                _entityModel.SaveChanges();
            }
            catch
            {
                return false;
            }
            return true;
        }

        #endregion

    }
}